package com.tca.cloud.standalone.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.Map;
import java.util.Set;

/**
 * @author zhouan
 * @Date 2021/7/31
 */
@Slf4j
public class ClassPathFeignScanner extends ClassPathBeanDefinitionScanner {

    /**
     * 后缀判断
     */
    private static final String PROXY_CLASS_SUFFIX = "Feign";

    public ClassPathFeignScanner(BeanDefinitionRegistry registry) {
        super(registry);
        registerFilters();
    }

    private void registerFilters() {
        // 添加过滤器, 判断是否需要扫描
        addIncludeFilter((metadataReader, metadataReaderFactory) ->
            metadataReader.getAnnotationMetadata().hasAnnotation(FeignClient.class.getName())
        );
    }

    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

        if (!beanDefinitions.isEmpty()) {
            try {
                processBeanDefinitions(beanDefinitions);
            } catch (Exception e) {
                log.error("scan error ", e);
                throw new RuntimeException(e);
            }
        }

        return beanDefinitions;
    }


    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) throws ClassNotFoundException {
        GenericBeanDefinition definition;
        for (BeanDefinitionHolder holder : beanDefinitions) {
            definition = (GenericBeanDefinition) holder.getBeanDefinition();
            // 这里使用的是: ScannedGenericBeanDefinition, 其beanClass目前还是字符串, 并不是Class对象,
            // 但是这里我们要用的是对象, 因此使用反射获取Class对象
            definition.getPropertyValues().add("feignClass", Class.forName(definition.getBeanClassName()));
            definition.getPropertyValues().add("beanFactory", getRegistry());
            // 添加相关属性
            // 获取注解属性
            AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) definition;
            AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
            Map<String, Object> attributes = annotationMetadata
                    .getAnnotationAttributes(
                            FeignClient.class.getCanonicalName());
            definition.getPropertyValues().add("prefix", getPrefix(attributes));
            definition.setBeanClass(FeignFactoryBean.class);
        }
    }

    /**
     * 从FeignClient中获取value作为prefix
     * @param attributes
     * @return
     */
    private String getPrefix(Map<String, Object> attributes) {
        String path = (String) attributes.get("path");
        return StringUtils.isEmpty(path)? "": path.endsWith("/")? path.substring(0, path.length() - 1): path;
    }




    /**
     * 重写方法: 根据Class判断是否需要扫描, 因为父类ClassPathScanningCandidateComponentProvider做了两层校验,
     * 所以需要配合上面的重载方法一起, 才会被扫描
     * 这里必须是接口, 且命名以Feign结尾
     * @param beanDefinition
     * @return
     */
    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() &&
                beanDefinition.getBeanClassName().endsWith(PROXY_CLASS_SUFFIX);
    }



}
